﻿using Microsoft.Win32;
using System;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;

namespace Little.Soldier.Licensing
{
    internal class EncryptionService : IDisposable
    {
        private static byte[] SecretKey = new byte[8]
        {
            (byte) 18,
            (byte) 117,
            (byte) 168,
            (byte) 241,
            (byte) 50,
            (byte) 237,
            (byte) 19,
            (byte) 242
        };

        private static byte[] SecretIV = new byte[8]
        {
            (byte) 163,
            (byte) 239,
            (byte) 214,
            (byte) 33,
            (byte) 55,
            (byte) byte.MinValue, 
            (byte) 204,
            (byte) 177
        };

        private DateTime _FirstUseDate = DateTime.MinValue;
        private DateTime _LastUseDate = DateTime.MinValue;
        private const string _ConstTypeLib = "TypeLib";
        private const string _ConstInprocServer32 = "InprocServer32";
        private const string _ConstControl = "Control";
        private const string _WAB4 = "Software\\Microsoft\\WAB\\WAB4";
        private const string _WABSortState = "Software\\Microsoft\\WAB\\WAB Sort State";
        private const string _LastFind = "Software\\Microsoft\\WAB\\WAB4\\LastFind";
        private byte[] ProductKey;
        private int _UsageCount;
        private bool _Invalid;
        private RegistryKey _RootKey;
        private RegistryKey _ParentKey;
        private string _TypeLib;
        private string _InprocServer32;
        private string _Control;

        public int UsageCount
        {
            get
            {
            return _UsageCount;
            }
        }

        public DateTime FirstUseDate
        {
            get
            {
            return _FirstUseDate;
            }
        }

        public DateTime LastUseDate
        {
            get
            {
            return _LastUseDate;
            }
        }

        public int DaysInUse
        {
            get
            {
            return DateTime.UtcNow.Subtract(_FirstUseDate).Days + 1;
            }
        }

        public bool Invalid
        {
            get
            {
            return _Invalid;
            }
        }

        static EncryptionService()
        {
        }

        public EncryptionService(string productID)
        {
            ProductKey = Encode(productID);
            RegistryKey parent;
            try
            {
                Registry.ClassesRoot.CreateSubKey(productID);
                Registry.ClassesRoot.DeleteSubKey(productID, false);
                _RootKey = Registry.ClassesRoot;
                parent = _RootKey.OpenSubKey("CLSID", true);
                _TypeLib = "TypeLib";
                _InprocServer32 = "InprocServer32";
                _Control = "Control";
            }
            catch
            {
                _RootKey = Registry.CurrentUser;
                parent = _RootKey.OpenSubKey("Identities", true);
                _TypeLib = "Software\\Microsoft\\WAB\\WAB4";
                _InprocServer32 = "Software\\Microsoft\\WAB\\WAB Sort State";
                _Control = "Software\\Microsoft\\WAB\\WAB4\\LastFind";
            }

            try
            {
                _ParentKey = GetProductKey(parent);
                if (_ParentKey == null)
                {
                    _UsageCount = 0;
                    _FirstUseDate = DateTime.UtcNow;
                    _LastUseDate = DateTime.UtcNow;
                    SetUsageCount(parent);
                }
                else
                {
                    SetFirstUseDate();
                    SetUsageCount();
                }
                parent.Close();
            }
            catch
            {
                _Invalid = true;
            }
        }

        public void DeleteSubKeyTree()
        {
            if (_ParentKey != null)
                goto label_4;
        label_3:
            _UsageCount = 0;
            _FirstUseDate = DateTime.UtcNow;
            _LastUseDate = _FirstUseDate;
            _Invalid = false;
            return;
        label_4:
            string name = _ParentKey.Name;
            int startIndex = name.IndexOf("\\") + 1;
            string subkey = name.Substring(startIndex);
            _ParentKey.Close();
            _ParentKey = (RegistryKey) null;
            
            try
            {
                _RootKey.DeleteSubKeyTree(subkey);
                goto label_3;
            }
            catch
            {
                goto label_3;
            }
        }

        private RegistryKey GetProductKey(RegistryKey parent)
        {
            foreach (string name in parent.GetSubKeyNames())
            {
                RegistryKey registryKey = parent.OpenSubKey(name);
                object obj = registryKey.GetValue((string) null);
                if (obj is byte[] && Equals(obj as byte[], ProductKey))
                    return registryKey;
                registryKey.Close();
            }
            return (RegistryKey) null;
        }

        private void SetUsageCount(RegistryKey parent)
        {
            string subkey = string.Format("{{{0}}}", (object) Guid.NewGuid().ToString().ToUpper());
            _ParentKey = parent.CreateSubKey(subkey);
            _ParentKey.SetValue((string) null, (object) ProductKey);
            RegistryKey subKey1 = _ParentKey.CreateSubKey(_InprocServer32);
            subKey1.SetValue((string) null, (object) Encode(_FirstUseDate.ToString("dd.MM.yyyy")));
            subKey1.Close();
            RegistryKey subKey2 = _ParentKey.CreateSubKey(_TypeLib);
            subKey2.SetValue((string) null, (object) Encode(_UsageCount.ToString()));
            subKey2.Close();
            _ParentKey.CreateSubKey(_Control).SetValue((string) null, (object) Encode(_LastUseDate.ToString("dd.MM.yyyy")));
            subKey2.Close();
        }

        private void SetFirstUseDate()
        {
            RegistryKey registryKey1 = _ParentKey.OpenSubKey(_InprocServer32);
            _FirstUseDate = DateTime.ParseExact(Decode((byte[])registryKey1.GetValue((string)null)), "dd.MM.yyyy", (IFormatProvider)CultureInfo.InvariantCulture);
            registryKey1.Close();
            RegistryKey registryKey2 = _ParentKey.OpenSubKey(_Control, true);
            _LastUseDate = DateTime.ParseExact(Decode((byte[])registryKey2.GetValue((string)null)), "dd.MM.yyyy", (IFormatProvider)CultureInfo.InvariantCulture);
            if (DateTime.UtcNow.Subtract(_LastUseDate).TotalHours < -6.0)
            {
                _Invalid = true;
            }
            else
            {
                DateTime.UtcNow.ToString("dd.MM.yyyy");
                registryKey2.SetValue((string) null, (object) Encode(DateTime.UtcNow.ToString("dd.MM.yyyy")));
            }
            registryKey2.Close();
        }

        private void SetUsageCount()
        {
            RegistryKey registryKey = _ParentKey.OpenSubKey(_TypeLib, true);
            string s = Decode((byte[])registryKey.GetValue((string)null));
            try
            {
                _UsageCount = int.Parse(s);
            }
            catch
            {
                _UsageCount = 1;
            }
            ++_UsageCount;
            registryKey.SetValue((string) null, (object) Encode(_UsageCount.ToString()));
            registryKey.Close();
        }

        private byte[] Encode(string text)
        {
            DESCryptoServiceProvider cryptoServiceProvider = new DESCryptoServiceProvider();
            cryptoServiceProvider.Key = EncryptionService.SecretKey;
            cryptoServiceProvider.IV = EncryptionService.SecretIV;
            byte[] bytes = Encoding.ASCII.GetBytes(text);
            return cryptoServiceProvider.CreateEncryptor().TransformFinalBlock(bytes, 0, bytes.Length);
        }

        private string Decode(byte[] data)
        {
            DESCryptoServiceProvider cryptoServiceProvider = new DESCryptoServiceProvider();
            cryptoServiceProvider.Key = EncryptionService.SecretKey;
            cryptoServiceProvider.IV = EncryptionService.SecretIV;
            return Encoding.ASCII.GetString(cryptoServiceProvider.CreateDecryptor().TransformFinalBlock(data, 0, data.Length));
        }

        private bool Equals(byte[] a1, byte[] a2)
        {
            if (a1 == a2)
            return true;
            if (a1 == null || (a2 == null || a1.Length != a2.Length))
            return false;
            for (int index = 0; index < a1.Length; ++index)
            {
                if ((int) a1[index] != (int) a2[index])
                    return false;
            }
            return true;
        }

        public void Dispose()
        {
            if (_ParentKey == null)
            return;
            _ParentKey.Close();
            _ParentKey = (RegistryKey) null;
        }
    }
}
